/*
 *  第六节 加载管理器
 */


import React,{useRef,useEffect,useCallback,useState} from 'react'
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader'
import * as THREE from 'three'
import { Progress } from 'antd'
import 'antd/dist/antd.css'
import './index.scss'

export default function Page() {

    const [loaded, setLoaded] = useState(0)
    const Body = useRef()
    // 当 React 附加了 ref 属性之后，ref.current 将直接指向 <button> DOM 元素实例。
    // 创建场景
    const Scene = useRef(new THREE.Scene()).current
    // 创建透视相机
    const Camera = useRef(new THREE.PerspectiveCamera()).current
    // 创建渲染器
    const Render = useRef(new THREE.WebGLRenderer(
        {antialias:true}
    )).current
    //创建物体组
    const Meshs = useRef([]).current
    // 创建灯光组
    const Lights = useRef([]).current
    // 我们的id等于这个循环函数返回的一个帧循环函数对应的ID
    const id = useRef(null)
    //
    const isDown = useRef(false)
    //
    const pi = useRef(80)
    //
    const R = useRef(90)
    // 地板
    const Floor = useRef() 

    const loaderFbx = useCallback(
        () => {

            const manager = new THREE.LoadingManager()
            manager.onLoad = () => setLoaded(100)
            manager.onStart = (_, loaded, total) => setLoaded(loaded / total)
            manager.onProgress = (_, loaded, total) => setLoaded(loaded / total)

            const loader = new FBXLoader(manager) 
            loader.setPath('/static/obj/')
            loader.load('gtx.fbx', (obj) => {
                obj.position.set(0, 0, 0)
                
                obj.scale.set(0.1, 0.1, 0.1)
                Scene.add(obj)
            })

        },
        [],
    )

    // 初始化灯光
    const createLight = useCallback(
        () => {
            // 太阳光
            const directionalLight = new THREE.DirectionalLight('#ffffff', 0.7)
            directionalLight.position.set(0,200,0)
            directionalLight.castShadow = true
            //投影产生阴影也是利用相机，而且相机默认范围比较小，所以要自定义
            directionalLight.shadow.camera.top = -10
            directionalLight.shadow.camera.bottom = 10
            directionalLight.shadow.camera.right = -10
            directionalLight.shadow.camera.left = 10
            directionalLight.shadow.mapSize.height = 1024
            directionalLight.shadow.mapSize.width = 1024

            // 环境光
            const ambientLight = new THREE.AmbientLight(0xffffff,0.5)

            // // 点光源
            // const pointLight = new THREE.PointLight( 0xffffff, 2, 8)
            // pointLight.position.set(0,5,0)

            Scene.add(
                directionalLight,
                ambientLight,
                // pointLight
                )
            Lights.push(
                directionalLight,
                ambientLight,
                // pointLight
                )
        },
        [],
    )


    // 创建地板
    const createFloor = useCallback(() => {
        const lambert = new THREE.MeshLambertMaterial({ color: '#fff' })
        const plane = new THREE.PlaneGeometry(300, 300)
        const mesh = new THREE.Mesh(plane, lambert)
        mesh.receiveShadow = true
        mesh.position.set(0, 0, 0)
        mesh.rotation.x = -90 / 180 * Math.PI
        mesh.receiveShadow = true
        Scene.add(mesh)
        Floor.current = mesh
    }, [])

    // 设置鼠标事件
    const wheel = useCallback(
        (event) => {
            if (event.deltaY>0) pi.current += 1
            else pi.current -= 1
            const x = pi.current*Math.cos(R.current/180*Math.PI)
            const y = Camera.position.y
            const z = pi.current*Math.sin(R.current/180*Math.PI)

            Camera.position.set(x,y,z)
            Camera.lookAt(0,0,0)
            console.log(event);
        },
        [],
    )


    const down = useCallback(
        () => {
            isDown.current = true
        },
        [],
    )
    const up = useCallback(
        () => {
            isDown.current = false
        },
        [],
    )
    const move = useCallback(
        (event) => {
            if (isDown.current === false ) return
            R.current -= event.movementX*0.5
            const x = pi.current*Math.cos(R.current/180*Math.PI)
            const y = Camera.position.y + event.movementY*0.1
            const z = pi.current*Math.sin(R.current/180*Math.PI)

            Camera.position.set(x,y,z)
            Camera.lookAt(0,0,0)
        },
        [],
    )
 
    // 初始化函数（设置宽高）
    // useCallback返回一个缓存的回调函数
    // 把内联回调函数及依赖项数组作为参数传入 useCallback，它将返回该回调函数的 memoized 版本，
    // 该回调函数仅在某个依赖项改变时才会更新。
    // 当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染（例如 shouldComponentUpdate）的子组件时，它将非常有用
    const init = useCallback(
        () => {
            Render.setSize(Body.current.offsetWidth,Body.current.offsetHeight)
            Render.shadowMap.enabled = true
            Render.setPixelRatio(window.devicePixelRatio)
            //设置相机参数
            Camera.aspect = Body.current.offsetWidth/Body.current.offsetHeight
            Camera.fov =45
            Camera.near = 1
            Camera.far = 1000
            Camera.position.set(0,3,pi.current)
            Camera.lookAt(0,0,0)
            Camera.updateProjectionMatrix()
        },
        [Render,Body],
    )

    // 渲染画面    
    const renderScene = useCallback(
        () => {
            Render.render(Scene,Camera)
            // Meshs.forEach(
            //     item =>{
            //         item.rotation.x += 1/180*Math.PI
            //         item.rotation.y += 1/180*Math.PI
            //     }
            // )
            id.current = window.requestAnimationFrame(() =>renderScene())
        },
        [Render,Meshs],
    )

    const setView = () =>{
        Render.setSize(document.body.clientWidth,document.body.clientHeight)
        Camera.aspect = Body.current.offsetWidth/Body.current.offsetHeight
        Camera.updateProjectionMatrix()
    }



    // .domElement : DOMElement
    // 一个canvas，渲染器在其上绘制输出。
    // 渲染器的构造函数会自动创建(如果没有传入canvas参数);你需要做的仅仅是像下面这样将它加页面里去:
    // document.body.appendChild( renderer.domElement );

    useEffect(() => {
        //渲染器的canvas添加到div里面去
        Body.current.append(Render.domElement)
        init()
        renderScene()
        createLight()
        createFloor()
        loaderFbx()
        document.addEventListener('resize',setView)
        
        // 销毁钩子
        return () => {
            // 生命周期卸载时调用
            //
            cancelAnimationFrame(id.current)
            document.removeEventListener('resize',setView)
            Meshs.forEach(
                item => {
                    Scene.remove(item)
                    item.geometry.dispose()
                    item.material.dispose()
                }
            )

            Floor.current && Scene.remove(Floor.current)

            Lights.forEach(
                item => Scene.remove(item)
            )
            Render.dispose()
            Scene.dispose()
                    
        }
    }, [])


    return (
        <div className="box">
        {loaded < 100 && (
            <div className="mask">
                <Progress percent={loaded} />
            </div>
        )}
        <div className="page" ref={Body} onMouseDown={down} onMouseUp={up} onMouseMove={move} onWheel={wheel}></div>
            
        </div>
    )
}
